// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <pollux/functions/registerer.h>
#include <pollux/functions/prestosql/date_time_functions.h>
#include <pollux/functions/prestosql/types/timestamp_with_time_zone_registration.h>

namespace kumo::pollux::functions {
    namespace {
        // Register timestamp + interval and interval + timestamp
        // functions for specified TTimestamp type and 2 supported interval types
        // (IntervalDayTime and IntervalYearMonth).
        // @tparam TTimestamp Timestamp or TimestampWithTimezone.
        template<typename TTimestamp>
        void registerTimestampPlusInterval(const std::string &name) {
            register_function<
                TimestampPlusInterval,
                TTimestamp,
                TTimestamp,
                IntervalDayTime>({name});
            register_function<
                TimestampPlusInterval,
                TTimestamp,
                TTimestamp,
                IntervalYearMonth>({name});
            register_function<
                IntervalPlusTimestamp,
                TTimestamp,
                IntervalDayTime,
                TTimestamp>({name});
            register_function<
                IntervalPlusTimestamp,
                TTimestamp,
                IntervalYearMonth,
                TTimestamp>({name});
        }

        // Register timestamp - IntervalYearMonth and timestamp - IntervalDayTime
        // functions for specified TTimestamp type.
        // @tparam TTimestamp Timestamp or TimestampWithTimezone.
        template<typename TTimestamp>
        void registerTimestampMinusInterval(const std::string &name) {
            register_function<
                TimestampMinusInterval,
                TTimestamp,
                TTimestamp,
                IntervalDayTime>({name});
            register_function<
                TimestampMinusInterval,
                TTimestamp,
                TTimestamp,
                IntervalYearMonth>({name});
        }

        void registerFromUnixtime(const std::string &name) {
            register_function<FromUnixtimeFunction, Timestamp, double>({name});
            register_function<
                FromUnixtimeFunction,
                TimestampWithTimezone,
                double,
                Varchar>({name});
            register_function<
                FromUnixtimeFunction,
                TimestampWithTimezone,
                double,
                int64_t,
                int64_t>({name});
        }

        void registerSimpleFunctions(const std::string &prefix) {
            // Date time functions.
            register_function<ToUnixtimeFunction, double, Timestamp>(
                {prefix + "to_unixtime"});
            register_function<ToUnixtimeFunction, double, TimestampWithTimezone>(
                {prefix + "to_unixtime"});

            registerFromUnixtime(prefix + "from_unixtime");

            register_function<DateFunction, Date, Varchar>({prefix + "date"});
            register_function<DateFunction, Date, Timestamp>({prefix + "date"});
            register_function<DateFunction, Date, TimestampWithTimezone>(
                {prefix + "date"});
            register_function<TimeZoneHourFunction, int64_t, TimestampWithTimezone>(
                {prefix + "timezone_hour"});

            register_function<TimeZoneMinuteFunction, int64_t, TimestampWithTimezone>(
                {prefix + "timezone_minute"});

            register_function<YearFunction, int64_t, Timestamp>({prefix + "year"});
            register_function<YearFunction, int64_t, Date>({prefix + "year"});
            register_function<YearFunction, int64_t, TimestampWithTimezone>(
                {prefix + "year"});
            register_function<YearFromIntervalFunction, int64_t, IntervalYearMonth>(
                {prefix + "year"});

            register_function<WeekFunction, int64_t, Timestamp>(
                {prefix + "week", prefix + "week_of_year"});
            register_function<WeekFunction, int64_t, Date>(
                {prefix + "week", prefix + "week_of_year"});
            register_function<WeekFunction, int64_t, TimestampWithTimezone>(
                {prefix + "week", prefix + "week_of_year"});
            register_function<QuarterFunction, int64_t, Timestamp>({prefix + "quarter"});
            register_function<QuarterFunction, int64_t, Date>({prefix + "quarter"});
            register_function<QuarterFunction, int64_t, TimestampWithTimezone>(
                {prefix + "quarter"});

            register_function<MonthFunction, int64_t, Timestamp>({prefix + "month"});
            register_function<MonthFunction, int64_t, Date>({prefix + "month"});
            register_function<MonthFunction, int64_t, TimestampWithTimezone>(
                {prefix + "month"});
            register_function<MonthFromIntervalFunction, int64_t, IntervalYearMonth>(
                {prefix + "month"});

            register_function<DayFunction, int64_t, Timestamp>(
                {prefix + "day", prefix + "day_of_month"});
            register_function<DayFunction, int64_t, Date>(
                {prefix + "day", prefix + "day_of_month"});
            register_function<DayFromIntervalFunction, int64_t, IntervalDayTime>(
                {prefix + "day", prefix + "day_of_month"});

            register_function<DateMinusInterval, Date, Date, IntervalDayTime>(
                {prefix + "minus"});
            register_function<DateMinusInterval, Date, Date, IntervalYearMonth>(
                {prefix + "minus"});
            register_function<DatePlusInterval, Date, Date, IntervalDayTime>(
                {prefix + "plus"});
            register_function<DatePlusInterval, Date, Date, IntervalYearMonth>(
                {prefix + "plus"});

            registerTimestampPlusInterval<Timestamp>({prefix + "plus"});
            registerTimestampMinusInterval<Timestamp>({prefix + "minus"});
            registerTimestampPlusInterval<TimestampWithTimezone>({prefix + "plus"});
            registerTimestampMinusInterval<TimestampWithTimezone>({prefix + "minus"});

            register_function<
                TimestampMinusFunction,
                IntervalDayTime,
                Timestamp,
                Timestamp>({prefix + "minus"});

            register_function<
                TimestampMinusFunction,
                IntervalDayTime,
                TimestampWithTimezone,
                TimestampWithTimezone>({prefix + "minus"});

            register_function<DayFunction, int64_t, TimestampWithTimezone>(
                {prefix + "day", prefix + "day_of_month"});
            register_function<DayOfWeekFunction, int64_t, Timestamp>(
                {prefix + "dow", prefix + "day_of_week"});
            register_function<DayOfWeekFunction, int64_t, Date>(
                {prefix + "dow", prefix + "day_of_week"});
            register_function<DayOfWeekFunction, int64_t, TimestampWithTimezone>(
                {prefix + "dow", prefix + "day_of_week"});
            register_function<DayOfYearFunction, int64_t, Timestamp>(
                {prefix + "doy", prefix + "day_of_year"});
            register_function<DayOfYearFunction, int64_t, Date>(
                {prefix + "doy", prefix + "day_of_year"});
            register_function<DayOfYearFunction, int64_t, TimestampWithTimezone>(
                {prefix + "doy", prefix + "day_of_year"});
            register_function<YearOfWeekFunction, int64_t, Timestamp>(
                {prefix + "yow", prefix + "year_of_week"});
            register_function<YearOfWeekFunction, int64_t, Date>(
                {prefix + "yow", prefix + "year_of_week"});
            register_function<YearOfWeekFunction, int64_t, TimestampWithTimezone>(
                {prefix + "yow", prefix + "year_of_week"});

            register_function<HourFunction, int64_t, Timestamp>({prefix + "hour"});
            register_function<HourFunction, int64_t, Date>({prefix + "hour"});
            register_function<HourFunction, int64_t, TimestampWithTimezone>(
                {prefix + "hour"});
            register_function<HourFromIntervalFunction, int64_t, IntervalDayTime>(
                {prefix + "hour"});

            register_function<LastDayOfMonthFunction, Date, Timestamp>(
                {prefix + "last_day_of_month"});
            register_function<LastDayOfMonthFunction, Date, Date>(
                {prefix + "last_day_of_month"});
            register_function<LastDayOfMonthFunction, Date, TimestampWithTimezone>(
                {prefix + "last_day_of_month"});

            register_function<MinuteFunction, int64_t, Timestamp>({prefix + "minute"});
            register_function<MinuteFunction, int64_t, Date>({prefix + "minute"});
            register_function<MinuteFunction, int64_t, TimestampWithTimezone>(
                {prefix + "minute"});
            register_function<MinuteFromIntervalFunction, int64_t, IntervalDayTime>(
                {prefix + "minute"});

            register_function<SecondFunction, int64_t, Timestamp>({prefix + "second"});
            register_function<SecondFunction, int64_t, Date>({prefix + "second"});
            register_function<SecondFunction, int64_t, TimestampWithTimezone>(
                {prefix + "second"});
            register_function<SecondFromIntervalFunction, int64_t, IntervalDayTime>(
                {prefix + "second"});

            register_function<MillisecondFunction, int64_t, Timestamp>(
                {prefix + "millisecond"});
            register_function<MillisecondFunction, int64_t, Date>(
                {prefix + "millisecond"});
            register_function<MillisecondFunction, int64_t, TimestampWithTimezone>(
                {prefix + "millisecond"});
            register_function<MillisecondFromIntervalFunction, int64_t, IntervalDayTime>(
                {prefix + "millisecond"});

            register_function<DateTruncFunction, Timestamp, Varchar, Timestamp>(
                {prefix + "date_trunc"});
            register_function<DateTruncFunction, Date, Varchar, Date>(
                {prefix + "date_trunc"});
            register_function<
                DateTruncFunction,
                TimestampWithTimezone,
                Varchar,
                TimestampWithTimezone>({prefix + "date_trunc"});
            register_function<DateAddFunction, Date, Varchar, int64_t, Date>(
                {prefix + "date_add"});
            register_function<DateAddFunction, Timestamp, Varchar, int64_t, Timestamp>(
                {prefix + "date_add"});
            register_function<
                DateAddFunction,
                TimestampWithTimezone,
                Varchar,
                int64_t,
                TimestampWithTimezone>({prefix + "date_add"});
            register_function<DateDiffFunction, int64_t, Varchar, Date, Date>(
                {prefix + "date_diff"});
            register_function<DateDiffFunction, int64_t, Varchar, Timestamp, Timestamp>(
                {prefix + "date_diff"});
            register_function<
                DateDiffFunction,
                int64_t,
                Varchar,
                TimestampWithTimezone,
                TimestampWithTimezone>({prefix + "date_diff"});
            register_function<DateFormatFunction, Varchar, Timestamp, Varchar>(
                {prefix + "date_format"});
            register_function<DateFormatFunction, Varchar, TimestampWithTimezone, Varchar>(
                {prefix + "date_format"});
            register_function<FormatDateTimeFunction, Varchar, Timestamp, Varchar>(
                {prefix + "format_datetime"});
            register_function<
                FormatDateTimeFunction,
                Varchar,
                TimestampWithTimezone,
                Varchar>({prefix + "format_datetime"});
            register_function<
                ParseDateTimeFunction,
                TimestampWithTimezone,
                Varchar,
                Varchar>({prefix + "parse_datetime"});
            register_function<DateParseFunction, Timestamp, Varchar, Varchar>(
                {prefix + "date_parse"});
            register_function<FromIso8601Date, Date, Varchar>(
                {prefix + "from_iso8601_date"});
            register_function<FromIso8601Timestamp, TimestampWithTimezone, Varchar>(
                {prefix + "from_iso8601_timestamp"});
            register_function<CurrentDateFunction, Date>({prefix + "current_date"});

            register_function<ToISO8601Function, Varchar, Date>({prefix + "to_iso8601"});
            register_function<ToISO8601Function, Varchar, Timestamp>(
                {prefix + "to_iso8601"});
            register_function<ToISO8601Function, Varchar, TimestampWithTimezone>(
                {prefix + "to_iso8601"});

            register_function<
                AtTimezoneFunction,
                TimestampWithTimezone,
                TimestampWithTimezone,
                Varchar>({prefix + "at_timezone"});

            register_function<ToMillisecondFunction, int64_t, IntervalDayTime>(
                {prefix + "to_milliseconds"});

            register_function<XxHash64DateFunction, int64_t, Date>(
                {prefix + "xxhash64_internal"});

            register_function<ParseDurationFunction, IntervalDayTime, Varchar>(
                {prefix + "parse_duration"});
        }
    } // namespace

    void registerDateTimeFunctions(const std::string &prefix) {
        registerTimestampWithTimeZoneType();

        registerSimpleFunctions(prefix);
    }
} // namespace kumo::pollux::functions
